﻿////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// <copyright>Copyright 2008-2011 Andreas Huber Doenni</copyright>
// Distributed under the Microsoft Public License (Ms-PL).
// See accompanying file License.txt or copy at http://mews.codeplex.com/license.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Mews.Net.Nntp.Cache
{
    using System;
    using System.Diagnostics.CodeAnalysis;

    /// <summary>Combines the article filter criteria.</summary>
    [SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes", Justification = "These methods are not needed.")]
    public struct InboundArticleFilter : IEquatable<InboundArticleFilter>
    {
        private static readonly InboundArticleFilter nullFilter = new InboundArticleFilter();

        private readonly bool? isOriginalBeingWatched;
        private readonly bool? hasOriginalBeenRead;
        private readonly bool? isFollowupBeingWatched;
        private readonly bool? hasFollowupBeenRead;

        /// <summary>Initializes a new instance of the <see cref="InboundArticleFilter"/> struct.</summary>
        public InboundArticleFilter(
            bool? isOriginalBeingWatched,
            bool? hasOriginalBeenRead,
            bool? isFollowupBeingWatched,
            bool? hasFollowupBeenRead)
        {
            this.isOriginalBeingWatched = isOriginalBeingWatched;
            this.hasOriginalBeenRead = hasOriginalBeenRead;
            this.isFollowupBeingWatched = isFollowupBeingWatched;
            this.hasFollowupBeenRead = hasFollowupBeenRead;
        }

        /// <summary>Gets a filter criterion for the articles returned by
        /// <see cref="IGroup.GetOriginalInboundArticles"/>.</summary>
        /// <value>The value <c>true</c> to let <see cref="IGroup.GetOriginalInboundArticles"/> return watched original
        /// articles; <c>false</c> to let <see cref="IGroup.GetOriginalInboundArticles"/> return unwatched original
        /// articles; <c>null</c> to let <see cref="IGroup.GetOriginalInboundArticles"/> return both watched and
        /// unwatched articles.</value>
        public bool? IsOriginalBeingWatched
        {
            get { return this.isOriginalBeingWatched; }
        }

        /// <summary>Gets a filter criterion for the articles returned by
        /// <see cref="IGroup.GetOriginalInboundArticles"/>.</summary>
        /// <value>The value <c>true</c> to let <see cref="IGroup.GetOriginalInboundArticles"/> return read original
        /// articles; <c>false</c> to let <see cref="IGroup.GetOriginalInboundArticles"/> return unread original
        /// articles; <c>null</c> to let <see cref="IGroup.GetOriginalInboundArticles"/> return both read and unread
        /// original articles.</value>
        public bool? HasOriginalBeenRead
        {
            get { return this.hasOriginalBeenRead; }
        }

        /// <summary>Gets a filter criterion for the articles returned by
        /// <see cref="IGroup.GetOriginalInboundArticles"/>.</summary>
        /// <value>The value <c>true</c> to let <see cref="IGroup.GetOriginalInboundArticles"/> return original articles
        /// with at least one watched follow-up article; <c>false</c> to let
        /// <see cref="IGroup.GetOriginalInboundArticles"/> return original articles with at least one unwatched
        /// follow-up article; <c>null</c> to let <see cref="IGroup.GetOriginalInboundArticles"/> return original
        /// articles with either watched or unwatched follow-up articles.</value>
        public bool? IsFollowupBeingWatched
        {
            get { return this.isFollowupBeingWatched; }
        }

        /// <summary>Gets a filter criterion for the articles returned by
        /// <see cref="IGroup.GetOriginalInboundArticles"/>.</summary>
        /// <value>The value <c>true</c> to let <see cref="IGroup.GetOriginalInboundArticles"/> return original articles
        /// with at least one read follow-up article; <c>false</c> to let
        /// <see cref="IGroup.GetOriginalInboundArticles"/> return original articles with at least one unread follow-up
        /// article; <c>null</c> to let <see cref="IGroup.GetOriginalInboundArticles"/> return original articles with
        /// either read or unread follow-up articles.</value>
        public bool? HasFollowupBeenRead
        {
            get { return this.hasFollowupBeenRead; }
        }

        /// <summary>See <see cref="IEquatable{T}.Equals"/>.</summary>
        public bool Equals(InboundArticleFilter other)
        {
            return (other.isOriginalBeingWatched == this.isOriginalBeingWatched) &&
                (other.hasOriginalBeenRead == this.hasOriginalBeenRead) &&
                (other.isFollowupBeingWatched == this.isFollowupBeingWatched) &&
                (other.hasFollowupBeenRead == this.hasFollowupBeenRead);
        }

        /// <summary>See <see cref="object.Equals(object)"/>.</summary>
        public override bool Equals(object obj)
        {
            var other = obj as InboundArticleFilter?;
            return other.HasValue && this.Equals(other.Value);
        }

        /// <summary>See <see cref="object.GetHashCode"/>.</summary>
        /// <returns>Always the value <c>0</c>.</returns>
        public override int GetHashCode()
        {
            // The standard procedure of XORing all the members of this class would create a hash method that wouldn't
            // return anything else than 0 and 1. Always returning 0 isn't much worse but probably an order of magnitude
            // faster. Of course, a more sensible approach could return a number that unambiguously maps to every
            // possible value this struct could ever have, which seems like overkill, given that this method will
            // probably never be used by production code.
            return 0;
        }

        /// <summary>Gets an <see cref="InboundArticleFilter"/> object that does not filter any articles.</summary>
        public static InboundArticleFilter NullFilter
        {
            get { return nullFilter; }
        }

        /// <summary>Determines whether two specified instances of <see cref="InboundArticleFilter"/> are equal.
        /// </summary>
        public static bool operator ==(InboundArticleFilter left, InboundArticleFilter right)
        {
            return left.Equals(right);
        }

        /// <summary>Determines whether two specified instances of <see cref="InboundArticleFilter"/> are not equal.
        /// </summary>
        public static bool operator !=(InboundArticleFilter left, InboundArticleFilter right)
        {
            return !left.Equals(right);
        }
    }
}
